package com.lagou.stage02;

import java.util.Arrays;
import java.util.Scanner;

public class GobangFight {
    private String[][] board = new String[16][16];
    private boolean flag = true;
    private String color;
    // 白棋棋子为 0， 黑棋棋子为 1
    private String chess;
    private Scanner sc = new Scanner(System.in);

    // 不接受外界对棋盘的初始化，所以无需有参构造方法，以及getAttr和setAttr方法
    // 无参构造方法中初始化棋盘
    GobangFight () {
        int i;
        for (i = 0; board.length > i; i++) {
            Arrays.fill(board[i], "+");
        }
    }

    // 绘制棋盘
    public void showBoard() {
        int i, j;
        char[] temp_arr = new char[]{' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        for (i = 0; board.length >= i; i++) {
            if (0 == i) {
                System.out.println("  0 1 2 3 4 5 6 7 8 9 a b c d e f");
                continue;
            }
            for (j = 0; board.length >= j; j++) {
                if (0 == j) {
                    System.out.print(temp_arr[i] + " ");
                    continue;
                }
                if (board.length == j) {
                    System.out.print(board[i-1][j-1]);
                } else {
                    System.out.print(board[i-1][j-1] + " ");
                }
            }
            System.out.println();
        }
    }

    // 开始游戏，直到获胜
    public void startPlay() {
        System.out.println("五子棋比赛正式开始！");
        showBoard();
        boolean over = false;
        while (!over) {
            over = play();
        }
    }

    //提醒下棋，获取下棋位置，判断该位置是否已有棋子，若有，则重新下棋，若没有，则绘制棋盘，判断当前下棋后有没有获胜
    private boolean play() {
        // 1. 提醒下棋，flag=true，代表白棋，flag=false，代表黑棋
        if (flag) {
            color = "白棋";
            chess = "0";
        } else {
            color = "黑棋";
            chess = "1";
        }

        // 2. 判断当前下棋的位置是否已有棋子，如果有，则重新下棋，直到当前下棋的位置没有棋子。
        boolean exist = true;
        int x = 0;
        int y = 0;
        while (exist) {
           System.out.println("请" + color + "下棋，输入下棋的横纵坐标 0-15");
           x = sc.nextInt();
           y = sc.nextInt();
           if (board.length <= x || board.length <= y) {
               System.out.println("请输入有效的坐标！");
           }
           else if (!"+".equals(board[x][y])) {
               System.out.println("该位置已有棋子!");
           } else {
               exist = false;
           }
        }

        // 3. 成功放置棋子，flag取反，确保下一次是另一方下棋。
        board[x][y] = chess;
        flag = !flag;

        // 4. 绘制棋盘
        showBoard();

        // 5. 判断当前下棋方有没有获胜
        if (ifWin(x, y)) {
            System.out.println("恭喜" + color + "获胜！！！");
            return true;
        }
        return false;
    }

    // 判断当前下棋后，是否获胜
    private boolean ifWin(int x, int y) {
        // 获胜的规则：
        // 第一种：当前所下棋子该行的同色系棋子连着的至少有5个；
        // 第二种：当前所下棋子该列的同色系棋子连着的至少有5个；
        // 第三种：当前所下棋子该左上到右下的同色系棋子连着的至少有5个；
        // 第四种：当前所下棋子该右上到左下的同色系棋子连着的至少有5个；
        // 其中"至少有5个"，表示当前棋子，可以是5个连着同色系的棋子中的任意位置。

        // i 表示当前所下棋子在预估的五个连续棋子中的位置
        // j 表示当前判断的棋子在预估的五个连续棋子中的位置
        // count_1 表示以当前棋子为中心，右边/下边/右下方向的连续同色系棋子的个数
        // count_2 表示以当前棋子为中心，左边/上边/左上方向的连续同色系棋子的个数
        int i, j;
        int count_1, count_2;

        // 判断当前所下棋子该行的同色系棋子连着的是否达到5个
        // 1. 行数=x
        // 2. 假设棋子所在的位置是五个连续同色系棋子的第一个位置，那么连续五子的第二个棋子位置是board[x][y+1]
        // 3. 判断是否是对应棋子颜色，若是，则判断第三个棋子board[x][y+2]的颜色
        // 4. 若不是，则假设当前棋子在五子中的第二个位置，以此类推
        count_1 = 0;
        count_2 = 0;
        outer: for (i = 1; 5 >= i; i ++) {
            // 以所下棋子为中心位置，往右的方向，判断下一个棋子是否为相同颜色
            for (j = 1; 6 - i > j; j++) {
                // 特殊情况: 如果预估第j个连续棋子的坐标值大于棋盘应用的范围，那么表示这个预估的下一个连续棋子无效，
                // 也就是表示，当前所下棋子并不是预计连续的五个棋子的i位置
                if (board.length <= y + j) {
                    break;
                }
                // 判断下一个棋子的颜色是否是该棋子的颜色，否则跳出循环，将该棋子置为五个连续棋子的下一位。
                if (!board[x][y + j].equals(chess)) {
                    break;
                }
                // 如果直到该棋子连续位置的第四个棋子（也就是五个连续棋子中第五个棋子）也是相同颜色，
                // 那么表示在该行存在五个同色系的连续的棋子，表示获胜。
                if (4 == j) {
                    return true;
                }
                count_1 += 1;
            }
            // 以所下棋子为中心位置，往左的方向，判断下一个棋子是否为相同颜色
            for (j = 1;  6 - i > j; j++) {
                // 因为以所下棋子为中心位置，往右的棋子都已经判断是否达到五个连续相同色系的棋子了，
                // 既然轮到判断往左的棋子，只要有一个不是相同色系，那么可以直接跳出判断整个大循环了（即判断该行是否有五个连续相同色系棋子）
                if (0 > y - j) {
                    break outer;
                }
                if (!board[x][y - j].equals(chess)) {
                    break outer;
                }
                // 以当前棋子为中心，左边和右边连续的相同色系的棋子一共有4个，则表示已经形成了连续五子，即获胜。
                if (count_1 + count_2 == 4) {
                    return true;
                }
                count_2 += 1;
            }
        }

        // 判断当前所下棋子该列的同色系棋子连着的是否达到5个，同判断同行的方法
        // 除了获取预估连续棋子的值时需要固定的是列值，往下board[x+j][y]，往上board[x-j][x]
        count_1 = 0;
        count_2 = 0;
        outer: for (i = 1; 5 >= i; i ++) {
            // 以所下棋子为中心位置，往下的方向，判断下一个棋子是否为相同颜色
            for (j = 1; 5 > j; j++) {
                if (board.length <= x + j) {
                    break;
                }
                if (!board[x + j][y].equals(chess)) {
                    break;
                }
                if (4 == j) {
                    return true;
                }
                count_1 += 1;
            }
            // 以所下棋子为中心位置，往上的方向，判断下一个棋子是否为相同颜色
            for (j = 1;  6 - i > j; j++) {
                if (0 > x - j) {
                    break outer;
                }
                if (!board[x - j][y].equals(chess)) {
                    break outer;
                }
                if (count_1 + count_2 == 4) {
                    return true;
                }
                count_2 += 1;
            }
        }

        // 判断当前所下棋子左上角到右下角的同色系棋子连着的是否达到5个，大致同判断同行的方法
        // 除了获取预估连续棋子的值时，右下是board[x+j][y+j]，左上board[x-j][y-j]
        count_1 = 0;
        count_2 = 0;
        outer: for (i = 1; 5 >= i; i ++) {
            // 以所下棋子为中心位置，往右下的方向，判断下一个棋子是否为相同颜色
            for (j = 1; 6 - i > j; j++) {
                if (board.length <= x + j || board.length <= y + j) {
                    break;
                }
                if (!board[x + j][y + j].equals(chess)) {
                    break;
                }
                if (4 == j) {
                    return true;
                }
                count_1 += 1;
            }
            // 以所下棋子为中心位置，往左上的方向，判断下一个棋子是否为相同颜色
            for (j = 1;  6 - i > j; j++) {
                if (0 > x - j || 0 > y - j) {
                    break outer;
                }
                if (!board[x - j][y - j].equals(chess)) {
                    break outer;
                }
                if (count_1 + count_2 == 4) {
                    return true;
                }
                count_2 += 1;
            }
        }

        // 判断当前所下棋子右上角角到左下角的同色系棋子连着的是否达到5个，大致同判断同行的方法
        // 除了获取预估连续棋子的值时，左下board[x+j][y-j], 右上是board[x-j][y+j]
        count_1 = 0;
        count_2 = 0;
        outer: for (i = 1; 5 >= i; i ++) {
            // 以所下棋子为中心位置，往左下的方向，判断下一个棋子是否为相同颜色
            for (j = 1; 6 - i > j; j++) {
                if (board.length <= x + j || 0 > y - j) {
                    break;
                }
                if (!board[x + j][y - j].equals(chess)) {
                    break;
                }
                if (4 == j) {
                    return true;
                }
                count_1 += 1;
            }
            // 以所下棋子为中心位置，往右上的方向，判断下一个棋子是否为相同颜色
            for (j = 1;  6 - i > j; j++) {
                if (0 > x - j || board.length <=  y + j) {
                    break outer;
                }
                if (!board[x - j][y + j].equals(chess)) {
                    break outer;
                }
                if (count_1 + count_2 == 4) {
                    return true;
                }
                count_2 += 1;
            }
        }
        return false;
    }
}
